package br.brpv.silvestrin.vendas.domain;

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Filter;
import android.widget.TextView;
import br.brpv.silvestrin.vendas.R;
import br.brpv.silvestrin.vendas.util.FormatString;

import java.util.*;


public class AddedItemAdapter extends ArrayAdapter<AddedItem> {
    private Context mContext;
    private int layoutResourceId;
    private ArrayList<AddedItem> mObjects = null, mDisplayedObjects = null;

    private Boolean mOutOfSync = false;
    private boolean mNotifyOnChange = false;

    private final Object mFilterLock = new Object();

    private Filter filter = null;

    public AddedItemAdapter(Context context, int layoutResourceId, ArrayList<AddedItem> data){
        super(context, layoutResourceId, data);

        this.mContext = context;
        this.layoutResourceId = layoutResourceId;

        mObjects = (data != null) ? data : new ArrayList<AddedItem>();
        mDisplayedObjects = new ArrayList<AddedItem>(mObjects);
    }

    public Context getContext() {
        return mContext;
    }

    public ArrayList<AddedItem> getObjects() {
        return mObjects;
    }

    protected Object getFilterLock() {
        return mFilterLock;
    }

    protected boolean isOutOfSync() {
        return mOutOfSync;
    }

    protected void setOutOfSync(boolean outOfSync) {
        mOutOfSync = outOfSync;
    }

    public void setNotifyOnChange(boolean notifyOnChange) {
        mNotifyOnChange = notifyOnChange;
    }

    public void add(AddedItem object) {
        synchronized (mObjects) {
            mObjects.add(object);
        }

        if (mNotifyOnChange) notifyDataSetChanged();
    }

    public void addAll(Collection<? extends AddedItem> collection) {
        synchronized (mObjects) {
            mObjects.addAll(collection);
        }

        if (mNotifyOnChange) notifyDataSetChanged();
    }

    public void addAll(AddedItem ... objects) {
        synchronized (mObjects) {
            for (AddedItem object : objects)
                mObjects.add(object);
        }

        if (mNotifyOnChange) notifyDataSetChanged();
    }

    public void insert(AddedItem object, int index) {
        synchronized (mObjects) {
            mObjects.add(index, object);
        }

        if (mNotifyOnChange) notifyDataSetChanged();
    }

    public void remove(AddedItem object) {
        synchronized (mObjects) {
            mObjects.remove(object);
        }

        if (mNotifyOnChange) notifyDataSetChanged();
    }

    public void clear() {
        synchronized (mObjects) {
            mObjects = new ArrayList<AddedItem>();
        }

        if (mNotifyOnChange) notifyDataSetChanged();
    }

    public void sort(Comparator<? super AddedItem> comparator) {
        synchronized (mObjects) {
            Collections.sort(mObjects, comparator);
        }

        if (mNotifyOnChange) notifyDataSetChanged();
    }

    @Override
    public void notifyDataSetChanged() {
        boolean reapplyFilter;

        synchronized (mFilterLock) {
            reapplyFilter = mOutOfSync = (mLastFilter != null);
        }

        if (reapplyFilter) {
            /* It would be amazing to only apply the filter to the
             * new elements, but since the collection could have
             * suffered unknown modifications, we can't.
             */

            getFilter().filter(mLastFilter);

        } else {
            synchronized (mObjects) {
                mDisplayedObjects = new ArrayList<AddedItem>(mObjects);
            }
        }

        doNotifyDataSetChanged();
    }

    protected void doNotifyDataSetChanged() {
        super.notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return mDisplayedObjects.size();
    }

    @Override
    public AddedItem getItem(int position) {
        return mDisplayedObjects.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View row = convertView;
        AddedItemHolder holder = null;

        if(row == null)
        {
            LayoutInflater inflater = ((Activity) mContext).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, parent, false);

            holder = new AddedItemHolder();
            holder.codItem = (TextView)row.findViewById(R.id.itemadded_cod_item);
            holder.itemCfgDesc = (TextView)row.findViewById(R.id.itemadded_desc_coletor);
            holder.itemAmount = (TextView)row.findViewById(R.id.itemadded_amount);
            holder.itemPrice = (TextView)row.findViewById(R.id.itemadded_price);
            holder.itemTotal = (TextView)row.findViewById(R.id.itemadded_price_total);

            row.setTag(holder);
        }
        else
        {
            holder = (AddedItemHolder)row.getTag();
        }

        AddedItem item = getItem(position);
        if(item != null){
            if(holder.codItem != null)holder.codItem.setText(String.valueOf(item.getCodItem()));
            if(holder.itemCfgDesc != null)holder.itemCfgDesc.setText(item.getDescription());
            if(holder.itemAmount != null)holder.itemAmount.setText(String.valueOf(item.getAmount()));
            if(holder.itemPrice != null)holder.itemPrice.setText(FormatString.formatValue(item.getPrice()));
            if(holder.itemTotal != null)holder.itemTotal.setText(FormatString.formatValue(item.getTotalLiquido()));
        }
        return row;
    }

    private static class AddedItemHolder {
        TextView codItem;
        TextView itemCfgDesc;
        TextView itemAmount;
        TextView itemPrice;
        TextView itemTotal;

    }

    @Override
    public Filter getFilter() {
        if(filter == null)
            filter = new ItemFilter();
        return filter;
    }

    private CharSequence mLastFilter = "";

    private class ItemFilter extends Filter {
        @Override
        protected FilterResults performFiltering(CharSequence constraintSeq) {
            ArrayList<AddedItem> filteredObjects;

            synchronized (mFilterLock) {
                if (!mOutOfSync && mLastFilter != null && mLastFilter.equals(constraintSeq))
                    return null;

                mLastFilter = constraintSeq;
            }

            synchronized (mObjects) {
                /* We'll make a copy of the list, so we can release the lock
                 * as soon as possible.partial
                 */
                filteredObjects = new ArrayList<AddedItem>(mObjects);
            }

            if (constraintSeq == null || constraintSeq.length() == 0) /* Part of the Filter contract */
                return resultsFromList(filteredObjects);

            String constraint = constraintSeq.toString().toLowerCase();

            ListIterator<AddedItem> it = filteredObjects.listIterator();

            while (it.hasNext()) {
                AddedItem item = it.next();
                StringTokenizer tokenizer = new StringTokenizer(constraint, " ");

                String desc = item.getDescription().toLowerCase();
                String cod = String.valueOf(item.getCodItem()).toLowerCase();

                boolean match = true;

                while(tokenizer.hasMoreTokens() && match) {
                    String token = tokenizer.nextToken();

                    if(!desc.contains(token) && !cod.contains(token)) {
                        match = false;
                    }
                }

                if (!match) it.remove();
            }

            return resultsFromList(filteredObjects);
        }

        @SuppressWarnings("unchecked")
		@Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            if (results != null) {
                mDisplayedObjects = (ArrayList<AddedItem>) results.values;
            }

            doNotifyDataSetChanged();
        }

        protected FilterResults resultsFromList(ArrayList<AddedItem> list) {
            FilterResults fr = new FilterResults();

            fr.values = list;
            fr.count = list.size();

            return fr;
        }
    }
}

